package com.tjhk.netty.mapping.handler;

import com.tjhk.netty.model.Message;
import io.netty.channel.Channel;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import java.lang.reflect.Method;
import java.lang.reflect.Type;


/**
 * handler 处理方法
 */
public abstract class Handler {
    /**
     * MESSAGE、CHANNEL固定参数位置
     */
    public static final int MESSAGE = 0;
    public static final int CHANNEL = 1;
    public final Object targetObject;
    public final Method targetMethod;
    public final int[] parameterTypes;
    public final boolean returnVoid;
    public final String desc;

    public Handler(Object targetObject, Method targetMethod, String desc) {
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
        this.returnVoid = targetMethod.getReturnType().isAssignableFrom(Void.TYPE);
        this.desc = desc;
        Type[] types = targetMethod.getGenericParameterTypes();
        int[] parameterTypes = new int[types.length];
        try {
            for (int i = 0; i < types.length; i++) {
                Type type = types[i];
                Class clazz;
                if (type instanceof ParameterizedTypeImpl)
                    clazz = (Class<?>) ((ParameterizedTypeImpl) type).getActualTypeArguments()[0];
                else
                    clazz = (Class<?>) type;

                if (Message.class.isAssignableFrom(clazz))
                    parameterTypes[i] = MESSAGE;
                if (Channel.class.isAssignableFrom(clazz))
                    parameterTypes[i] = CHANNEL;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        this.parameterTypes = parameterTypes;
    }

    public <T extends Message> T invoke(T request, Channel channel) throws Exception {
        Object[] args = new Object[parameterTypes.length];

        for (int i = 0; i < parameterTypes.length; i++) {
            int type = parameterTypes[i];
            switch (type) {
                case Handler.MESSAGE:
                    args[i] = request;
                    break;
                case Handler.CHANNEL:
                    args[i] = channel;
                    break;
            }
        }
        return (T) targetMethod.invoke(targetObject, args);
    }
    public <T extends Message> T invoke(T request) throws Exception {
        return (T) targetMethod.invoke(targetObject, request);
    }
}
